from PyQt5.QtWidgets import QWidget, QVBoxLayout, QTabWidget, QTextEdit, QPlainTextEdit, QFileDialog, QTextEdit, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QDialog, QListWidget, QListWidgetItem, QPushButton, QAbstractItemView
from PyQt5.QtGui import QFont, QPainter, QTextFormat, QColor
from PyQt5.QtCore import Qt, QRect, QSize, QEvent, QPropertyAnimation, QEasingCurve, QTimer
import os
from language.python_highlighter import PythonHighlighter
from language.java_highlighter import JavaHighlighter
from language.javascript_highlighter import JavaScriptHighlighter
from language.typescript_highlighter import TypeScriptHighlighter
from language.html_highlighter import HTMLHighlighter
from language.css_highlighter import CSSHighlighter
from language.c_highlighter import CHighlighter
from language.xml_highlighter import XMLHighlighter
from language.yaml_highlighter import YAMLHighlighter
from language.json_highlighter import JSONHighlighter
from language.properties_highlighter import PropertiesHighlighter
from language.ini_highlighter import INIHighlighter
from language.markdown_highlighter import MarkdownHighlighter
from language.simple_highlighter import SimpleHighlighter

class LineNumberArea(QWidget):
    """行号区域部件"""
    def __init__(self, editor):
        super().__init__(editor)
        self.editor = editor
    
    def sizeHint(self):
        return QSize(self.editor.line_number_area_width(), 0)
    
    def paintEvent(self, event):
        self.editor.line_number_area_paint_event(event)

class CodeEditor(QPlainTextEdit):
    """支持行号显示的代码编辑器"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.line_number_area = LineNumberArea(self)
        self.blockCountChanged.connect(self.update_line_number_area_width)
        self.updateRequest.connect(self.update_line_number_area)
        self.cursorPositionChanged.connect(self.highlight_current_line)
        
        # 初始更新行号区域宽度
        self.update_line_number_area_width(0)
        
        # 高亮当前行
        self.highlight_current_line()
    
    def line_number_area_width(self):
        """计算行号区域宽度"""
        digits = 1
        max_num = max(1, self.blockCount())
        while max_num >= 10:
            max_num //= 10
            digits += 1
        
        # 计算宽度：数字宽度 + 边距
        space = 3 + self.fontMetrics().horizontalAdvance('9') * digits
        return space
    
    def update_line_number_area_width(self, new_block_count):
        """更新行号区域宽度"""
        self.setViewportMargins(self.line_number_area_width(), 0, 0, 0)
    
    def update_line_number_area(self, rect, dy):
        """更新行号区域"""
        if dy:
            self.line_number_area.scroll(0, dy)
        else:
            self.line_number_area.update(0, rect.y(), self.line_number_area.width(), rect.height())
        
        if rect.contains(self.viewport().rect()):
            self.update_line_number_area_width(0)
    
    def resizeEvent(self, event):
        """重设大小事件"""
        super().resizeEvent(event)
        
        cr = self.contentsRect()
        self.line_number_area.setGeometry(QRect(cr.left(), cr.top(), self.line_number_area_width(), cr.height()))
    
    def line_number_area_paint_event(self, event):
        """绘制行号区域"""
        painter = QPainter(self.line_number_area)
        
        # 根据主题设置行号区域背景色
        # 使用主题管理器获取当前主题，而不是解析样式表
        from PyQt5.QtWidgets import QApplication
        app = QApplication.instance()
        
        # 尝试从主窗口获取主题管理器
        main_window = None
        for widget in app.topLevelWidgets():
            if hasattr(widget, 'theme_manager'):
                main_window = widget
                break
        
        if main_window and hasattr(main_window.theme_manager, 'get_current_theme'):
            current_theme = main_window.theme_manager.get_current_theme()
            if current_theme == 'dark':  # 深色主题
                painter.fillRect(event.rect(), QColor('#2d2d2d'))
                painter.setPen(QColor('#888888'))
            else:  # 浅色主题
                painter.fillRect(event.rect(), QColor('#f5f5f5'))
                painter.setPen(QColor('#666666'))
        else:
            # 备用方案：根据窗口背景色判断
            bg_color = self.palette().window().color()
            if bg_color.lightness() < 128:  # 深色背景
                painter.fillRect(event.rect(), QColor('#2d2d2d'))
                painter.setPen(QColor('#888888'))
            else:  # 浅色背景
                painter.fillRect(event.rect(), QColor('#f5f5f5'))
                painter.setPen(QColor('#666666'))
        
        painter.setFont(self.font())
        
        # 获取第一个可见块
        block = self.firstVisibleBlock()
        block_number = block.blockNumber()
        top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
        bottom = top + self.blockBoundingRect(block).height()
        
        # 绘制可见块的行号
        while block.isValid() and top <= event.rect().bottom():
            if block.isVisible() and bottom >= event.rect().top():
                number = str(block_number + 1)
                painter.drawText(0, int(top), self.line_number_area.width() - 3, 
                               self.fontMetrics().height(),
                               Qt.AlignRight, number)
            
            block = block.next()
            top = bottom
            bottom = top + self.blockBoundingRect(block).height()
            block_number += 1
    
    def highlight_current_line(self):
        """高亮当前行"""
        extra_selections = []
        
        if not self.isReadOnly():
            selection = QTextEdit.ExtraSelection()
            
            # 根据主题设置当前行高亮颜色
            from PyQt5.QtWidgets import QApplication
            app = QApplication.instance()
            
            # 尝试从主窗口获取主题管理器
            main_window = None
            for widget in app.topLevelWidgets():
                if hasattr(widget, 'theme_manager'):
                    main_window = widget
                    break
            
            if main_window and hasattr(main_window.theme_manager, 'get_current_theme'):
                current_theme = main_window.theme_manager.get_current_theme()
                if current_theme == 'dark':  # 深色主题
                    # 使用半透明蓝色高亮，不会覆盖字体
                    line_color = QColor('#0078d4')
                    line_color.setAlpha(40)  # 设置透明度
                else:  # 浅色主题
                    # 使用半透明蓝色高亮，不会覆盖字体
                    line_color = QColor('#0078d4')
                    line_color.setAlpha(30)  # 设置透明度
            else:
                # 备用方案：根据窗口背景色判断
                bg_color = self.palette().window().color()
                if bg_color.lightness() < 128:  # 深色背景
                    line_color = QColor('#0078d4')
                    line_color.setAlpha(40)  # 设置透明度
                else:  # 浅色背景
                    line_color = QColor('#0078d4')
                    line_color.setAlpha(30)  # 设置透明度
                
            selection.format.setBackground(line_color)
            selection.format.setProperty(QTextFormat.FullWidthSelection, True)
            selection.cursor = self.textCursor()
            selection.cursor.clearSelection()
            extra_selections.append(selection)
        
        self.setExtraSelections(extra_selections)

class HiddenTabsPopup(QDialog):
    """显示隐藏标签列表的半透明弹窗"""
    def __init__(self, workspace):
        super().__init__(workspace)
        self.workspace = workspace
        self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint)
        self.setFixedSize(350, 600)
        self.setAttribute(Qt.WA_TranslucentBackground)

        layout = QVBoxLayout(self)
        layout.setContentsMargins(8, 8, 8, 8)
        layout.setSpacing(8)

        self.list_widget = QListWidget(self)
        self.list_widget.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
        try:
            from PyQt5.QtWidgets import QScroller
            QScroller.grabGesture(self.list_widget, QScroller.LeftMouseButtonGesture)
        except Exception:
            pass

        layout.addWidget(self.list_widget)

        # 根据主题调整样式，提升可见度
        self.update_style_by_theme()

        # 注意：在 Windows 透明窗口上启用图形阴影可能导致渲染错误（UpdateLayeredWindowIndirect），
        # 因此不使用 QGraphicsDropShadowEffect。通过更高不透明度与加粗边框提升可见度。

        self.fade_anim = QPropertyAnimation(self, b"windowOpacity")
        self.fade_anim.setDuration(200)
        self.fade_anim.setEasingCurve(QEasingCurve.OutCubic)
        self.setWindowOpacity(0.0)

        self.list_widget.itemClicked.connect(self.on_item_clicked)

    def on_item_clicked(self, item):
        index = item.data(Qt.UserRole)
        if index is not None:
            self.workspace.tab_widget.setCurrentIndex(index)
            self.hide_with_animation()

    def update_items(self):
        self.list_widget.clear()
        hidden = self.workspace.get_hidden_tab_indices()
        for idx in hidden:
            title = self.workspace.tab_widget.tabText(idx)
            it = QListWidgetItem(title)
            it.setData(Qt.UserRole, idx)
            self.list_widget.addItem(it)

    def show_at_right_of_tabbar(self):
        # 顶部位置：与标签栏底部齐平
        tab_bar = self.workspace.tab_widget.tabBar()
        bottom_left_global = tab_bar.mapToGlobal(tab_bar.rect().bottomLeft())
        y = bottom_left_global.y()

        # 水平位置：弹窗右侧与主窗口右侧对齐
        main_window = self.workspace.window()
        main_top_left_global = main_window.mapToGlobal(main_window.rect().topLeft())
        right_edge_x = main_top_left_global.x() + main_window.rect().width()
        x = right_edge_x - self.width()

        self.move(x, y)
        self.update_items()
        self.show()
        self.show_with_animation()

    def show_with_animation(self):
        self.fade_anim.stop()
        self.setWindowOpacity(0.0)
        self.fade_anim.setStartValue(0.0)
        self.fade_anim.setEndValue(1.0)
        self.fade_anim.start()

    def hide_with_animation(self):
        self.fade_anim.stop()
        self.fade_anim.setStartValue(self.windowOpacity())
        self.fade_anim.setEndValue(0.0)
        self.fade_anim.start()
        QTimer.singleShot(self.fade_anim.duration(), self.hide)

    def update_style_by_theme(self):
        from PyQt5.QtWidgets import QApplication
        app = QApplication.instance()
        main_window = None
        for w in app.topLevelWidgets():
            if hasattr(w, 'theme_manager'):
                main_window = w
                break
        theme = None
        if main_window and hasattr(main_window.theme_manager, 'get_current_theme'):
            theme = main_window.theme_manager.get_current_theme()
        if theme == 'dark':
            self.setStyleSheet(
                "QDialog{background-color: rgba(245,245,245,1.0); border:2px solid #007acc; border-radius:6px;}"
                "QListWidget{background: transparent; color: #eeeeee; border: 1px solid gary;}"
                "QListWidget::item{padding:6px 10px; color: #000 !important; background-color: #555555;}"
                "QListWidget::item:selected{background-color: rgba(0,122,204,1); color: #eeeeee;}"
            )
        else:
            self.setStyleSheet(
                "QDialog{background-color: rgba(20,20,20,1.0); border:2px solid #007acc; border-radius:6px;}"
                "QListWidget{background: transparent; color: #e0e0e0; border: none;}"
                "QListWidget::item{padding:6px 10px;}"
                "QListWidget::item:selected{background-color: rgba(0,122,204,1); color: #eeeeee;}"
            )

class Workspace(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setup_ui()
        # 监听 TabBar 的布局与尺寸变化，以便实时更新溢出状态
        self.tab_widget.tabBar().installEventFilter(self)
    
    def setup_ui(self):
        """设置工作区界面"""
        # 主布局
        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(0)
        
        # 标签页组件
        self.tab_widget = QTabWidget()
        self.tab_widget.setTabsClosable(True)
        self.tab_widget.setMovable(True)

        # 连接标签页关闭信号
        self.tab_widget.tabCloseRequested.connect(self.close_tab)

        # 溢出指示按钮与弹窗
        corner_container = QWidget()
        corner_layout = QHBoxLayout(corner_container)
        corner_layout.setContentsMargins(15, 0, 0, 0)  # 保留15px间距
        corner_layout.setSpacing(0)

        self.overflow_btn = QPushButton("...")
        self.overflow_btn.setFixedSize(24, 24)
        self.overflow_btn.setVisible(False)
        self.overflow_btn.setToolTip("显示隐藏的标签页")
        self.overflow_btn.setStyleSheet(
            "QPushButton{background-color: rgba(50,50,50,1); color:#ccc; border:1px solid #444; border-radius:4px;}"
            "QPushButton:hover{background-color: rgba(255,255,255,1);}"
        )
        self.overflow_btn.clicked.connect(self.toggle_hidden_popup)
        corner_layout.addWidget(self.overflow_btn)
        self.tab_widget.setCornerWidget(corner_container, Qt.TopRightCorner)

        self.hidden_popup = HiddenTabsPopup(self)
        
        # 创建默认标签页
        self.create_default_tab()

        main_layout.addWidget(self.tab_widget)

        # 不设置硬编码样式，由主题管理器统一管理
        # 初始更新溢出状态
        QTimer.singleShot(0, self.update_overflow_state)
    
    def create_default_tab(self):
        """创建默认标签页"""
        # 创建代码编辑器（支持行号显示）
        editor = CodeEditor()
        
        # 设置编辑器字体
        font = QFont("Consolas", 12)
        editor.setFont(font)
        
        # 不设置硬编码样式，由主题管理器统一管理
        
        # 设置默认文本
        editor.setPlainText("# 欢迎使用 CodeIdea\n# 这是一个代码编辑器区域\n\nprint('Hello, CodeIdea!')\n")
        
        # 添加到标签页
        self.tab_widget.addTab(editor, "Untitled-1")
        QTimer.singleShot(0, self.update_overflow_state)
    
    def add_tab(self, filename="Untitled"):
        """添加新标签页"""
        editor = CodeEditor()
        font = QFont("Consolas", 12)
        editor.setFont(font)
        # 不设置硬编码样式，由主题管理器统一管理
        
        self.tab_widget.addTab(editor, filename)
        self.tab_widget.setCurrentWidget(editor)
        QTimer.singleShot(0, self.update_overflow_state)
        
        return editor
    
    def get_current_editor(self):
        """获取当前编辑器"""
        return self.tab_widget.currentWidget()
    
    def get_current_file_path(self):
        """获取当前文件的完整路径"""
        editor = self.get_current_editor()
        if editor and hasattr(editor, 'file_path'):
            return editor.file_path
        return None
    
    def open_file(self, file_path):
        """打开文件"""
        try:
            # 检查是否是二进制文件（如.class, .exe, .dll等）
            binary_extensions = ['.class', '.exe', '.dll', '.so', '.dylib', '.o', '.pyc', 
                                '.jar', '.war', '.zip', '.tar', '.gz', '.jpg', '.png', 
                                '.gif', '.bmp', '.ico', '.pdf', '.doc', '.docx']
            file_ext = os.path.splitext(file_path)[1].lower()
            
            if file_ext in binary_extensions:
                # 二进制文件提示用户选择
                from PyQt5.QtWidgets import QMessageBox
                reply = QMessageBox.question(
                    None,
                    "二进制文件",
                    f"检测到 {file_ext} 文件可能是二进制文件，\n以文本方式打开可能显示乱码。\n\n是否仍要以文本方式打开？",
                    QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No
                )
                if reply == QMessageBox.No:
                    return False
                # 用户选择继续打开，继续执行后续代码
            
            # 尝试用UTF-8编码打开文件，如果失败则尝试其他编码
            try:
                with open(file_path, 'r', encoding='utf-8') as file:
                    content = file.read()
            except UnicodeDecodeError:
                # 如果UTF-8失败，尝试GBK编码
                try:
                    with open(file_path, 'r', encoding='gbk') as file:
                        content = file.read()
                except UnicodeDecodeError:
                    # 如果GBK也失败，尝试用Latin-1（几乎不会失败）
                    with open(file_path, 'r', encoding='latin-1', errors='replace') as file:
                        content = file.read()
            
            # 检查文件是否已经打开
            filename = os.path.basename(file_path)
            for i in range(self.tab_widget.count()):
                if self.tab_widget.tabText(i) == filename:
                    # 文件已打开，切换到该标签页
                    self.tab_widget.setCurrentIndex(i)
                    # 通知状态栏更新文件路径显示
                    if hasattr(self, 'parent') and hasattr(self.parent(), 'status_bar'):
                        self.parent().status_bar.set_file_path(file_path)
                    return True
            
            # 创建新标签页
            editor = CodeEditor()
            font = QFont("Consolas", 12)
            editor.setFont(font)
            # 不设置硬编码样式，由主题管理器统一管理
            
            # 设置文件内容
            editor.setPlainText(content)
            
            # 根据文件后缀静默加载语法高亮
            self.apply_syntax_highlighting(editor, file_path)
            
            # 添加标签页并设置文件名
            self.tab_widget.addTab(editor, filename)
            self.tab_widget.setCurrentWidget(editor)
            
            # 保存文件路径信息
            editor.file_path = file_path
            
            # 通知状态栏更新文件路径显示
            if hasattr(self, 'parent') and hasattr(self.parent(), 'status_bar'):
                self.parent().status_bar.set_file_path(file_path)
            QTimer.singleShot(0, self.update_overflow_state)
            
            return True
        except Exception as e:
            print(f"打开文件失败: {e}")
            return False
    
    def close_tab(self, index):
        """关闭指定索引的标签页"""
        if index < 0 or index >= self.tab_widget.count():
            return False
        
        # 获取当前编辑器
        editor = self.tab_widget.widget(index)
        if not editor:
            return False
        
        # 检查是否有未保存的更改
        if hasattr(editor, 'file_path') and editor.file_path:
            # 如果是已保存的文件，直接关闭
            self.tab_widget.removeTab(index)
        else:
            # 如果是未保存的文件，检查是否有内容
            if editor.toPlainText().strip():
                # 有内容，需要提示用户保存
                from PyQt5.QtWidgets import QMessageBox
                reply = QMessageBox.question(
                    self, 
                    "保存文件", 
                    "文件有未保存的更改，是否保存？",
                    QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel,
                    QMessageBox.Save
                )
                
                if reply == QMessageBox.Save:
                    # 用户选择保存
                    if self.save_current_file():
                        self.tab_widget.removeTab(index)
                    else:
                        # 保存失败，不关闭标签页
                        return False
                elif reply == QMessageBox.Discard:
                    # 用户选择不保存
                    self.tab_widget.removeTab(index)
                else:
                    # 用户取消操作
                    return False
            else:
                # 没有内容，直接关闭
                self.tab_widget.removeTab(index)
        
        # 如果关闭后没有标签页了，创建一个新的默认标签页
        if self.tab_widget.count() == 0:
            self.create_default_tab()
        QTimer.singleShot(0, self.update_overflow_state)
        
        return True

    # ======== 溢出与弹窗相关逻辑 ========
    def update_overflow_state(self):
        """根据标签总宽度与可视区域判断是否显示溢出指示"""
        bar = self.tab_widget.tabBar()
        # 计算标签总宽度
        total = 0
        for i in range(bar.count()):
            total += bar.tabRect(i).width()
        # 可用宽度（扣除右侧预留空间：15px + 指示按钮宽度）
        reserved = 15 + (self.overflow_btn.width() if self.overflow_btn.isVisible() else 24)
        available = max(0, bar.width() - reserved)
        show_indicator = total > available
        self.overflow_btn.setVisible(show_indicator)
        # 弹窗显示时内容实时刷新
        if self.hidden_popup.isVisible():
            self.hidden_popup.update_items()

    def get_hidden_tab_indices(self):
        """返回被截断不可见的标签索引列表"""
        bar = self.tab_widget.tabBar()
        reserved = 15 + (self.overflow_btn.width() if self.overflow_btn.isVisible() else 24)
        available = max(0, bar.width() - reserved)
        hidden = []
        width_sum = 0
        for i in range(bar.count()):
            w = bar.tabRect(i).width()
            width_sum += w
            if width_sum > available:
                hidden.append(i)
        return hidden

    def toggle_hidden_popup(self):
        if self.hidden_popup.isVisible():
            self.hidden_popup.hide_with_animation()
        else:
            self.hidden_popup.show_at_right_of_tabbar()

    def resizeEvent(self, event):
        super().resizeEvent(event)
        QTimer.singleShot(0, self.update_overflow_state)

    def eventFilter(self, obj, event):
        if obj == self.tab_widget.tabBar() and event.type() in (QEvent.Resize, QEvent.LayoutRequest):
            QTimer.singleShot(0, self.update_overflow_state)
        return super().eventFilter(obj, event)
    
    def close_current_tab(self):
        """关闭当前标签页"""
        current_index = self.tab_widget.currentIndex()
        if current_index >= 0:
            return self.close_tab(current_index)
        return False
    
    def save_current_file(self):
        """保存当前文件"""
        editor = self.get_current_editor()
        if not editor:
            return False
        
        # 检查是否有文件路径
        if hasattr(editor, 'file_path'):
            try:
                with open(editor.file_path, 'w', encoding='utf-8') as file:
                    file.write(editor.toPlainText())
                return True
            except Exception as e:
                print(f"保存文件失败: {e}")
                return False
        else:
            # 如果没有文件路径，执行另存为
            return self.save_as_current_file()
    
    def save_as_current_file(self):
        """另存为当前文件"""
        editor = self.get_current_editor()
        if not editor:
            return False
        
        file_path, _ = QFileDialog.getSaveFileName(
            self, 
            "保存文件", 
            "", 
            "文本文件 (*.txt *.py *.js *.html *.css *.json *.xml *.md);;所有文件 (*.*)"
        )
        
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as file:
                    file.write(editor.toPlainText())
                
                # 更新文件路径和标签页标题
                editor.file_path = file_path
                filename = os.path.basename(file_path)
                current_index = self.tab_widget.currentIndex()
                self.tab_widget.setTabText(current_index, filename)
                
                # 重新应用语法高亮
                self.apply_syntax_highlighting(editor, file_path)
                
                return True
            except Exception as e:
                print(f"保存文件失败: {e}")
                return False
        return False
    
    def apply_syntax_highlighting(self, editor, file_path):
        """根据文件后缀静默加载语法高亮"""
        # 获取文件后缀
        _, file_extension = os.path.splitext(file_path)
        
        # 清除现有的语法高亮器
        if hasattr(editor, 'highlighter'):
            editor.highlighter.setDocument(None)
        
        # 根据文件后缀应用不同的语法高亮
        if file_extension.lower() == '.py':
            # Python文件：应用Python语法高亮
            editor.highlighter = PythonHighlighter(editor.document())
        elif file_extension.lower() == '.java':
            # Java文件：应用Java语法高亮
            editor.highlighter = JavaHighlighter(editor.document())
        elif file_extension.lower() == '.js':
            # JavaScript文件：应用JavaScript语法高亮
            editor.highlighter = JavaScriptHighlighter(editor.document())
        elif file_extension.lower() == '.ts':
            # TypeScript文件：应用TypeScript语法高亮
            editor.highlighter = TypeScriptHighlighter(editor.document())
        elif file_extension.lower() in ['.html', '.htm']:
            # HTML文件
            editor.highlighter = HTMLHighlighter(editor.document())
        elif file_extension.lower() == '.css':
            # CSS文件
            editor.highlighter = CSSHighlighter(editor.document())
        elif file_extension.lower() in ['.c', '.h']:
            # C文件
            editor.highlighter = CHighlighter(editor.document())
        elif file_extension.lower() in ['.xml']:
            # XML文件
            editor.highlighter = XMLHighlighter(editor.document())
        elif file_extension.lower() in ['.yaml', '.yml']:
            # YAML文件
            editor.highlighter = YAMLHighlighter(editor.document())
        elif file_extension.lower() in ['.json']:
            # JSON文件
            editor.highlighter = JSONHighlighter(editor.document())
        elif file_extension.lower() in ['.properties', '.prop']:
            # Properties文件
            editor.highlighter = PropertiesHighlighter(editor.document())
        elif file_extension.lower() in ['.ini', '.cfg', '.conf']:
            # INI文件
            editor.highlighter = INIHighlighter(editor.document())
        elif file_extension.lower() in ['.md', '.markdown']:
            # Markdown文件
            editor.highlighter = MarkdownHighlighter(editor.document())
        else:
            # 其他文件类型：应用简单高亮器
            editor.highlighter = SimpleHighlighter(editor.document())